package org.linlinjava.litemall.core.payment.paypal.service.impl;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.core.constant.LumiereCloverConstant;
import org.linlinjava.litemall.core.notify.AwsNotifyService;
import org.linlinjava.litemall.core.notify.NoticeHelper;
import org.linlinjava.litemall.core.payment.DefaultCurType;
import org.linlinjava.litemall.core.payment.PaymentResponseCode;
import org.linlinjava.litemall.core.payment.paypal.config.PaypalPaymentIntent;
import org.linlinjava.litemall.core.payment.paypal.config.PaypalPaymentMethod;
import org.linlinjava.litemall.core.payment.paypal.config.PaypalPaymentState;
import org.linlinjava.litemall.core.payment.paypal.service.PaypalService;
import org.linlinjava.litemall.core.util.AdminCouponDateUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.beans.Constants;
import org.linlinjava.litemall.db.domain.*;
import org.linlinjava.litemall.db.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.paypal.api.payments.Amount;
import com.paypal.api.payments.DetailedRefund;
import com.paypal.api.payments.Payer;
import com.paypal.api.payments.Payment;
import com.paypal.api.payments.PaymentExecution;
import com.paypal.api.payments.RedirectUrls;
import com.paypal.api.payments.RefundRequest;
import com.paypal.api.payments.RelatedResources;
import com.paypal.api.payments.Sale;
import com.paypal.api.payments.Transaction;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;


/**
 * @author ：stephen
 * @date ：Created in 2020/4/2 16:23
 * @description：TODO
 */
@Service
public class CardPaypalServiceImpl implements PaypalService {
    private final Log logger = LogFactory.getLog(CardPaypalServiceImpl.class);

    @Autowired
    private APIContext apiContext;
    @Resource
    private LitemallGiftCardOrderService litemallGiftCardOrderService;

    @Autowired
    private AwsNotifyService awsNotifyService;

    @Autowired
    private NoticeHelper noticeHelper;

    @Autowired
    private LumiereCouponStrategyService lumiereCouponStrategyService;

    @Autowired
    private LitemallUserService litemallUserService;
    @Autowired
    private LumiereCouponUserService lumiereCouponUserService;
    @Autowired
    private LumiereCouponService lumiereCouponService;

    /**
     * 收到你付款
     *
     * @param userId     用户id
     * @param orderId    订单id
     * @param successUrl 成功url
     * @param cancelUrl  取消url
     * @return {@link Object}
     */
    @Override
    public Object getPayment(Integer userId, Integer orderId, String successUrl, String cancelUrl,String message) {
        LitemallGiftCardOrder order = litemallGiftCardOrderService.findById(orderId);


        if(order == null){
            return ResponseUtil.fail(PaymentResponseCode.ORDER_UNKNOWN, "订单不存在");
        }
        // TODO 判断竟然不相等
        if(order.getPayStatus() != Constants.PAY_STATUS_DOING){
            return ResponseUtil.fail(PaymentResponseCode.ORDER_INVALID_OPERATION, "订单不能支付");
        }
        BigDecimal total = order.getAmount();
        Amount amount = new Amount();
        amount.setCurrency(DefaultCurType.CAD.getType());
        amount.setTotal(String.format("%.2f", total));

        Transaction transaction = new Transaction();
        transaction.setDescription("lumiere gift card pay");
        transaction.setAmount(amount);


        List<Transaction> transactions = new ArrayList<>();
        transactions.add(transaction);

        Payer payer = new Payer();
        payer.setPaymentMethod(PaypalPaymentMethod.paypal.toString());

        Payment payment = new Payment();
        payment.setIntent(PaypalPaymentIntent.sale.toString());
        payment.setPayer(payer);
        payment.setTransactions(transactions);
        RedirectUrls redirectUrls = new RedirectUrls();
        redirectUrls.setCancelUrl(cancelUrl);
        redirectUrls.setReturnUrl(successUrl);
        payment.setRedirectUrls(redirectUrls);


        try {
            logger.debug("success url is " + redirectUrls.getReturnUrl());
            logger.debug("cancel url is " + redirectUrls.getCancelUrl());
            logger.debug("进入支付之前的日志");
            Payment rtn =  payment.create(apiContext);
            logger.debug("进入支付页面之后的日志");
            if(null == rtn.getId() || !PaypalPaymentState.created.toString().equals(rtn.getState())){
                return ResponseUtil.fail(PaymentResponseCode.ORDER_INVALID_OPERATION, "订单不能支付");
            }
            String paymentId = rtn.getId();
            LitemallGiftCardOrder update = new LitemallGiftCardOrder();
            //保存paymentId作为PayPal的商户订单号
            update.setId(orderId);
            update.setOutTradeNo(paymentId);
            update.setMessage(message);
//            rtn.get
            //状态为进行中
            update.setPayStatus(Constants.PAY_STATUS_DOING);
            if (litemallGiftCardOrderService.paddingPay(update, order) == 0) {
                return ResponseUtil.updatedDateExpired();
            }
            return rtn;
        } catch (PayPalRESTException e) {
            e.printStackTrace();
            return ResponseUtil.fail(PaymentResponseCode.ORDER_INVALID_OPERATION, "订单不能支付");
        }
    }

    @Override
    public Object executePayment(String paymentId, String payerId) throws PayPalRESTException {
        Payment payment = new Payment();
        payment.setId(paymentId);
        PaymentExecution paymentExecute = new PaymentExecution();
        paymentExecute.setPayerId(payerId);
        Payment rtn = payment.execute(apiContext, paymentExecute);

        // 交易号
        String transationId = rtn.getTransactions().get(0).getRelatedResources().get(0).getSale().getId();
        if(rtn.getState().equals("approved")){
            LitemallGiftCardOrder order = litemallGiftCardOrderService.findByOutTradeNo(paymentId);
            List<Transaction> transactions = rtn.getTransactions();
            BigDecimal totalFee = new BigDecimal(0.00);
            if(transactions != null){
                for(Transaction transaction: transactions){
                    String total = transaction.getAmount().getTotal();
                    BigDecimal b = new BigDecimal(total);
                    b.setScale(2, BigDecimal.ROUND_DOWN);
                    totalFee = totalFee.add(b);
                }
            }
            if (order == null) {
                return ResponseUtil.fail(PaymentResponseCode.ORDER_UNKNOWN, "订单不存在");
            }

            // 检查这个订单是否已经处理过
            if (order.getPayStatus() == Constants.PAY_STATUS_DONE || order.getPayId() != null) {
                return ResponseUtil.fail(PaymentResponseCode.PAYMENT_FAIL, "订单已经处理成功!");
            }

            // 检查支付订单金额
            if (!totalFee.toString().equals(order.getAmount().toString())) {
                return ResponseUtil.fail(PaymentResponseCode.PAYMENT_FAIL, " : 支付金额不符合 totalFee=" + totalFee);
            }

            LitemallGiftCardOrder update = new LitemallGiftCardOrder();
            update.setPayType(Constants.PAY_TYPE_PAYPAL);
            update.setCurrency(DefaultCurType.CAD.getType());
            update.setTransationId(transationId);        //交易号，退款时需要用到
            update.setPayId(payment.getId());            //付款编号，获取详情是需要用到？
            update.setPayerId(payerId);
            update.setPayTime(LocalDateTime.now());
            update.setPayStatus(Constants.PAY_STATUS_DONE);

            logger.debug("更新订单信息");
            if (litemallGiftCardOrderService.payDone(order, update) == 0) {
                return ResponseUtil.updatedDateExpired();
            }
            //赠送充值策略
            List<LumiereCouponStrategy> lumiereCouponStrategies = lumiereCouponStrategyService.list(Wrappers.lambdaQuery(LumiereCouponStrategy.class)
                    .eq(LumiereCouponStrategy::getCouponType, 7)
                    .eq(LumiereCouponStrategy::getStatus, 1));
            if (!lumiereCouponStrategies.isEmpty()){
                List couponName = CollUtil.newArrayList();
                lumiereCouponStrategies.stream().forEach(couponStrategie->{
                    //开始发券
                    if(order.getAmount().compareTo(couponStrategie.getRechargeAmount())!=-1){
                        long nextId = IdUtil.getSnowflake(2, 2).nextId();
                        LocalDateTime[] localDateTimes = AdminCouponDateUtil.calculateExpirationTime(couponStrategie);
                        LitemallUser litemallUser = litemallUserService.findById(order.getUserId());
                        LumiereCouponUser lumiereCouponUser = LumiereCouponUser.builder()
                                .userId(order.getUserId())
                                .strategyId(couponStrategie.getId())
                                .couponId(couponStrategie.getCouponId())
                                .userName(litemallUser.getUsername())
                                .startTime(localDateTimes[0])
                                .endTime(localDateTimes[1])
                                .code(String.valueOf(nextId))
                                .addTime(LocalDateTime.now())
                                .updateTime(LocalDateTime.now())
                                .deleted(false)
                                .build();
                        LumiereCoupon lumiereCoupon = lumiereCouponService.getById(couponStrategie.getCouponId());
                        couponName.add(lumiereCoupon.getName());
                        lumiereCouponUserService.save(lumiereCouponUser);
                    }
                });
                //发送赠送券 系统消息给用户
                noticeHelper.noticeUser( Constants.MSG_TYPE_OTHER, LumiereCloverConstant.WX_BALANCE_CLOVER_RECHARGE_TITLE_NOTICEHELPER,
                        String.format(LumiereCloverConstant.WX_BALANCE_PAYPAL_RECHARGE_GIVE_COUPON_NOTICEHELPER, order.getAmount(),couponName), order.getUserId(), null);
            }

            //用户增加积分
/*            List<LitemallOrderGoods> orderGoods = litemallOrderGoodsService.queryByOid(order.getId());
            BigDecimal giveAwayPoints = new BigDecimal(0.00);
            for(LitemallOrderGoods item : orderGoods){
                LitemallGoods goods = goodsService.findById(item.getId());
                giveAwayPoints = giveAwayPoints.add(goods.getGiveAwayPoints().multiply(new BigDecimal(item.getNumber())));
            }
            if(giveAwayPoints.compareTo(new BigDecimal(0.00)) > 0){
                LitemallUser user = userService.findById(order.getUserId());
                if(user != null){
                    LitemallUser litemallUser = new LitemallUser();
                    litemallUser.setId(order.getUserId());
                    litemallUser.setPoints(user.getPoints().add(giveAwayPoints));
                    userService.updateById(litemallUser);
                }

            }*/

            // 这里微信的短信平台对参数长度有限制，所以将订单号只截取后6位
//            notifyService.notifySmsTemplateSync(order.getMobile(), NotifyType.PAY_SUCCEED, new String[]{order.getOrderSn().substring(8, 14)});
            return rtn;
        }else{
            logger.info("gift card executePayment error : "+ rtn.toJSON());
            return ResponseUtil.fail(PaymentResponseCode.PAYMENT_FAIL, "支付失败");
        }
    }

    @Override
    public boolean refund(Integer orderId) {
        LitemallGiftCardOrder order = litemallGiftCardOrderService.findById(orderId);
        // ###Sale
        // A sale transaction.
        // Create a Sale object with the
        // given sale transaction id.

        // ###Refund
        // A refund transaction.
        // Use the amount to create
        // a refund object
        RefundRequest refund = new RefundRequest();
        // ###Amount
        // Create an Amount object to
        // represent the amount to be
        // refunded. Create the refund object, if the refund is partial
        Amount amount = new Amount();
        amount.setCurrency(order.getCurrency());
        amount.setTotal(String.format("%.2f", order.getAmount()));
        refund.setAmount(amount);
//        refund.setReason(order.getDescription());


        try {
            if(StringUtils.isEmpty(order.getTransationId())) {
                //注意这段代码，获取saleId
                Payment payment = Payment.get(apiContext, order.getPayId());
                Transaction transaction = payment.getTransactions().get(0);
                RelatedResources resources = transaction.getRelatedResources().get(0);
                String id = resources.getSale().getId();
                order.setTransationId(id);
            }

            // ### Api Context
            // Pass in a `ApiContext` object to authenticate
            // the call and to send a unique request id
            // (that ensures idempotency). The SDK generates
            // a request id if you do not pass one explicitly.

            // Refund by posting to the APIService
            // using a valid AccessToken
            Sale sale = new Sale();
            sale.setId(order.getTransationId());
            DetailedRefund res = sale.refund(apiContext, refund);
            if(res.getState().equals(PaypalPaymentState.completed.toString())){
                return true;
            }
            return false;
        } catch (PayPalRESTException e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean refund(String paymentId, String payerId) {
        LitemallGiftCardOrder order = litemallGiftCardOrderService.findByOutTradeNo(paymentId);
        return refund(order.getId());
    }
}
